Skip to content

feat(minipay): redirect to Account page and hide Governance tab until staked#480

Open
pahor167 wants to merge 16 commits intomainfrom
pahor/miniapp-account-landing-governance-gating
Open

feat(minipay): redirect to Account page and hide Governance tab until staked#480
pahor167 wants to merge 16 commits intomainfrom
pahor/miniapp-account-landing-governance-gating

Conversation

@pahor167
Copy link
Copy Markdown
Contributor

Summary

  • MiniPay users now land on the Account page instead of the Staking page (redirect via router.replace)
  • Governance tab is hidden in MiniPay until the user has staked (stCELO balance > 0)
  • Both changes are gated behind isMiniPay — no impact on the regular web app

Changes

  • src/app/page.tsx: Added useEffect redirect to /account for MiniPay users
  • src/components/nav/NavBar.tsx: Added hideInMiniPayUntilStaked flag on Governance link, checked against useStCELOBalance in both desktop and mobile nav

Test plan

  • Open in MiniPay → should land on Account page, not Staking
  • Open in regular browser → should land on Staking page as before
  • In MiniPay with no stCELO → Governance tab should be hidden
  • In MiniPay with stCELO balance → Governance tab should be visible
  • Regular browser → Governance tab always visible regardless of staking status

… staked

MiniPay users now land on the Account page instead of the Staking page,
and the Governance tab is hidden until they have staked (stCELO balance > 0).
Both changes are gated behind the isMiniPay flag and don't affect the
regular web app.
@vercel
Copy link
Copy Markdown

vercel bot commented Apr 14, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
celo-mondo Ready Ready Preview Apr 15, 2026 1:25pm

Request Review

@jcortejoso
Copy link
Copy Markdown
Member

jcortejoso commented Apr 14, 2026

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b7e0634dc9

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/app/page.tsx
Comment on lines +25 to +26
if (isMiniPay) {
router.replace('/account');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate MiniPay home redirect on wallet availability

This redirect is unconditional for MiniPay, so it also runs when no wallet address is connected (or before wagmi finishes hydrating). In that state, /account immediately redirects back to / via usePageInvariant(!!address, '/') in src/app/account/page.tsx, creating a //account navigation loop for MiniPay users and leaving no stable landing page. Redirecting only after account readiness (or when address is present) avoids that loop.

Useful? React with 👍 / 👎.

Comment thread src/components/nav/NavBar.tsx Outdated
const { address } = useAccount();
const trackEvent = useTrackEvent();
const isMiniPay = useIsMiniPay();
const { stCELOBalances } = useStCELOBalance(address);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid stCELO polling for non-MiniPay navigation

The new useStCELOBalance(address) call executes for every connected user, even outside MiniPay, because the hook is not gated and its internal queries are enabled by !!address with periodic refetching. Since hideInMiniPayUntilStaked is only checked later in the filters, regular web users now do recurring stCELO RPC reads (and can see related error toasts) despite this feature being MiniPay-only.

Useful? React with 👍 / 👎.

MiniPay auto-connects the wallet asynchronously, so the redirect to
/account was hitting the usePageInvariant guard before the address was
available, causing a 404/redirect loop. Skip the guard for MiniPay users
since they will always auto-connect.
…ount

Prevents the validators table from flashing briefly before the redirect
to /account completes.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: ea1f25361b

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

<ul className="flex list-none items-center justify-center space-x-6 overflow-hidden">
{LINKS(!!address)
.filter((l) => !(isMiniPay && l.hideInMiniPay))
.filter((l) => !(isMiniPay && l.hideInMiniPayUntilStaked && !hasStaked))
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Gate MiniPay governance on CELO stake, not stCELO balance

The new MiniPay Governance filter depends on hasStaked, but hasStaked is derived only from useStCELOBalance; MiniPay users are forced into CELO mode (src/utils/useStakingMode.tsx:23) and stake through the CELO flow, so they can complete staking while stCELOBalances.total remains 0n. In that path, Governance stays hidden indefinitely instead of reappearing after the user has staked.

Useful? React with 👍 / 👎.

Comment thread src/app/page.tsx

const { groups, totalVotes } = useValidatorGroups();

if (isMiniPay) return null;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid running validator query before MiniPay early return

MiniPay now redirects away from / and returns null, but this happens only after useValidatorGroups() runs, so each MiniPay home visit still triggers the full validator-group fetch path and its error-toast side effects (useToastError in src/features/validators/useValidatorGroups.ts) for data that is never rendered. This adds avoidable RPC load and can show irrelevant errors during redirect.

Useful? React with 👍 / 👎.

Pavel Hornak added 2 commits April 14, 2026 14:06
Shows a CTA card with staking description and Stake button for MiniPay
users who haven't staked yet. Uses the existing CtaCard pattern and
opens the StakeStCELO transaction flow on click.
MiniPay users are forced into CELO mode and stake through the CELO
flow, so stCELO balance stays 0. Switch to useLockedBalance (locked
CELO in governance) for the Governance tab visibility check and the
Account page staking CTA.
Extract StakingPage into a child component so that useValidatorGroups()
is never called when isMiniPay is true. Avoids unnecessary RPC calls
and error toasts during the redirect to /account.
Replace 2-column CtaCard grid with a single-column stacked layout to
avoid empty space next to the text on mobile.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 496abbb163

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/app/account/page.tsx
Comment on lines +423 to +425
onClick={() =>
showTxModal(TransactionFlowType.StakeStCELO, { action: StakeActionType.Stake })
}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Open CELO lock flow from the MiniPay stake CTA

The new StakeCeloCta button opens TransactionFlowType.StakeStCELO, but Governance visibility in MiniPay is gated by lockedBalance > 0n in src/components/nav/NavBar.tsx. In the MiniPay scenario this means a user with no locked CELO can follow this CTA, complete staking, and still never satisfy the nav gate (because stCELO deposit does not increase LockedGold), so the CTA fails to unlock the Governance access it promises.

Useful? React with 👍 / 👎.

When the lock form is shown as a prerequisite (e.g. user has no locked
CELO and tries to stake), the modal header now correctly shows "Lock CELO"
instead of "Stake CELO", and the onConfirmed callback is passed through
so the success confirmation screen appears after the lock transaction.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 436424ea33

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

) {
Component = <LockForm showTip={true} />;
header = 'Lock CELO';
Component = <LockForm showTip={true} onConfirmed={onConfirmed} />;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Preserve original action after prerequisite lock completes

Passing onConfirmed into the prerequisite LockForm changes the flow for Stake, Delegate, Upvote, and Vote when the user has no locked CELO: after a successful lock, confirmationDetails is set and TransactionFlow switches to TransactionConfirmation (which closes/reloads) instead of returning to the originally requested form. This regresses the existing one-modal progression and forces users to restart the action (often losing context like proposal-specific voting intent).

Useful? React with 👍 / 👎.

Comment on lines +34 to +35
const { lockedBalance } = useLockedBalance(address);
const hasStaked = lockedBalance > 0n;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Gate locked-balance polling to MiniPay-only rendering

The navbar now calls useLockedBalance(address) unconditionally for every connected user, but hasStaked is only used for the MiniPay-specific governance filter. Because useLockedBalance enables periodic refetching (BALANCE_REFRESH_INTERVAL, 5s) and emits toast errors on failures, non-MiniPay users now incur continuous extra RPC reads and can see unrelated error toasts for a feature branch they never use.

Useful? React with 👍 / 👎.

Two changes:

1. TransactionFlow: check confirmationDetails before prerequisite
   checks. Previously the lockedBalance <= 0 check took priority,
   so after a successful prerequisite lock the LockForm re-rendered
   instead of showing the confirmation screen (until the 5s balance
   refetch caught up).

2. useLockedStatus: add refetchInterval (5s) matching useLockedBalance.
   Without this, the locked balance on the account page relied on a
   page reload to refresh — which may not clear React Query cache in
   MiniPay's WebView, leaving "Total locked" stuck at 0.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 63eaf3729d

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +34 to +35
const { lockedBalance } = useLockedBalance(address);
const hasStaked = lockedBalance > 0n;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Compute MiniPay staking gate from signing account

The MiniPay Governance gate is based on useLockedBalance(address), which checks only the connected wallet, but governance eligibility in this codebase is resolved through vote-signer indirection (useVoteSignerToAccount in src/features/governance/hooks/useVotingStatus.ts and src/features/transactions/TransactionFlow.tsx). If a MiniPay wallet is a vote signer for another staked account, address can have 0n locked balance while the signing account still has voting power, so this nav filter hides Governance for a user who can otherwise participate.

Useful? React with 👍 / 👎.

- ?minipay or ?minipay=true forces MiniPay UI in a normal browser
- ?address=0x... views any account read-only (hides write actions)
- Both can be combined: ?address=0x...&minipay
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 920befe601

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/app/account/page.tsx
Comment on lines +134 to +136
{!isReadOnly && !isVoteSigner && (
<LockButtons className="flex justify-between md:hidden" mode={mode} />
)}
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Disable write actions when account is marked read-only

In read-only mode (address query param present), this change hides only the lock buttons but still renders the transactional tables below. Those tables expose actions like stake/unstake/activate via useTransactionModal (see ActiveStakesTable), and TransactionFlow executes against the connected wallet from useAccount(), not the overridden address being viewed. That creates a misleading state where users can initiate wallet transactions while viewing another account’s data.

Useful? React with 👍 / 👎.

Comment on lines +5 to +8
const [address] = useState(() => {
if (typeof window === 'undefined') return undefined;
const param = new URLSearchParams(window.location.search).get('address');
if (param && isAddress(param)) return param as Address;
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-read address query param on navigation updates

useAddressParam snapshots window.location.search once in useState and never updates it, so client-side transitions between different /account?address=... URLs will keep showing the first address until a full reload. This can leave the account page showing stale balances/labels after in-app navigation or browser history changes.

Useful? React with 👍 / 👎.

MiniPay's provider intercepts eth_sendTransaction to add feeCurrency
for gas abstraction (CIP-64). This reconstruction appears to drop the
value field, causing payable calls like lock() to send 0 CELO.

By setting feeCurrency explicitly before the tx reaches the provider,
MiniPay may preserve the original transaction fields including value.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 3ee8a1309a

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/app/page.tsx

useEffect(() => {
if (isMiniPay) {
router.replace('/account');
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve minipay override when redirecting from home

The home redirect drops the current query string, so a session opened at /?minipay is immediately sent to /account without minipay, and useIsMiniPay() on the destination page falls back to window.ethereum?.isMiniPay. In a normal browser this disables the override after one navigation (and can bounce users back to / when no wallet is connected), which breaks the new query-param MiniPay emulation flow.

Useful? React with 👍 / 👎.

Comment thread src/utils/useIsMiniPay.ts
Comment on lines +4 to +7
const [isMiniPay] = useState(() => {
if (typeof window === 'undefined') return false;
const params = new URLSearchParams(window.location.search);
if (params.has('minipay')) return params.get('minipay') !== 'false';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Re-read MiniPay override when the URL query changes

This hook snapshots window.location.search once in component state and never updates it, so client-side transitions (or back/forward) between URLs that differ in ?minipay keep using the stale initial value. As a result, MiniPay-gated behavior (redirects, nav filtering, fee-currency logic, mode forcing) can remain incorrect after navigation.

Useful? React with 👍 / 👎.

Setting feeCurrency explicitly did not prevent MiniPay from dropping the
value field. This is a MiniPay provider bug with payable function calls.
Adds feeCurrency to writeContract for MiniPay with isMiniPay in the
useCallback deps array and a debug log to confirm value and isMiniPay
state at tx submission time.
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b100d05010

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment on lines +86 to +88
window.alert(
`isMiniPay=${isMiniPay} value=${args.value} feeCurrency=${(txArgs as any).feeCurrency}`,
);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Remove debug alert from transaction send path

The newly added window.alert(...) runs for every non-WalletConnect transaction submission, including regular web users when isMiniPay is false. This inserts a blocking browser modal into the core write flow, forces an extra user interaction before each transaction, and can break automated or embedded wallet flows that expect uninterrupted submission. This should not ship in production transaction code.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: c466ad9ed5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread src/app/account/page.tsx
Comment on lines +102 to +103
{isReadOnly ? (
<div className="align-right flex flex-col items-end">
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep vote-signer banner when rendering read-only account

This new isReadOnly branch masks the existing vote-signer UI path, so /?address=<vote-signer> now shows “Viewing account ” even though most balances and tables are still derived from signingFor (via useBalance(signingFor), useLockedStatus(signingFor), etc.). In that scenario the page labels one account while rendering another account’s staking/governance data, which is misleading for shared read-only links. Preserve the vote-signer context (or align data sourcing with the displayed address) in read-only mode.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants